package com.platform.comm.common.batch;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.platform.batch.base.exception.JobExecuteFailedException;
import com.platform.comm.common.batch.mapper.ComResultRowMapper;
import com.platform.comm.common.service.ComImportService;
import com.platform.comm.entity.CommBatchInfoEntity;
import com.platform.comm.service.ICommDataInfoBiz;
import com.platform.comm.util.SpringBeansUtil;
import com.platform.vo.BatchResult;
import com.platform.vo.ExcelCfgDto;
import com.platform.vo.ExcelColCfgDto;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.springframework.batch.item.database.JdbcPagingItemReader;
import org.springframework.batch.item.database.Order;
import org.springframework.batch.item.database.builder.JdbcPagingItemReaderBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.StringJoiner;
import java.util.stream.Collectors;


/**
 * Description: 通用批量导出上下文
 *
 * @author lch Created on 2020/1/8
 **/
@Log4j2
@Component
@JsonIgnoreProperties(value = {"handler", "hibernateLazyInitializer", "fieldHandler"})
public class ComBatchExportContext {
    @JsonIgnore
    private final ICommDataInfoBiz commDataImpBiz;

    private Map<String, Object> parametersMap = Maps.newHashMap();
    private Map<String, Object> comItemMap = Maps.newHashMap();
    private Set<String> enumFieldSet = Sets.newHashSet();

    @Getter
    private ExcelCfgDto exportConfig;

    @Getter
    @Setter
    private String filePath;

    private static final int FETCH_SIZE = 10000;
    private static final int PAGE_SIZE = 10000;

    @Getter
    private Map<String, String> params = Maps.newHashMap();
    private String tableName;

    @Getter
    private BatchResult batchResult = new BatchResult();
    private CommBatchInfoEntity dirBatchInfoEntity;
    private ComImportService comImportService;

    public ComBatchExportContext(@Autowired(required = false) ICommDataInfoBiz commDataImpBiz) {
        this.commDataImpBiz = commDataImpBiz;
    }

    public ComBatchExportContext buildConfig(String moduleName) {
        String module = StringUtils.substringBefore(moduleName, "Controller");
        this.exportConfig = commDataImpBiz.initImpConfig(module, ICommDataInfoBiz.EXPORT);
        this.tableName = exportConfig.getTableName();

        return this;
    }

    /**
     * 绑定模块
     */
    public ComBatchExportContext bindParameterMap(Map<String, String> parameterMap) {
        String moduleName = parameterMap.get("moduleName");
        String module = StringUtils.substringBefore(moduleName, "Controller");

        Optional.ofNullable(moduleName).orElseThrow(() -> new JobExecuteFailedException("模块名称不能为空"));
        this.params = parameterMap;

        String bizType = module + "ImportServiceImpl";
        this.comImportService = (ComImportService) SpringBeansUtil.getBean(bizType);

        return this;
    }

    public void build() {
        try {
          this.processFieldNamesMapping();
          this.processDictFieldCache();
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
            throw new JobExecuteFailedException(ex.getMessage());
        }
    }

    /**
     * 获取导出数据的表名
     *
     * @return 数据的表名
     */
    public String getTableName() {
        return this.tableName;
    }

    /**
     * 获取字段与字段内容映射关系
     */
    private Map<String, Object> getFieldNameMapping() {
        return this.comItemMap;
    }

    /**
     * 获取排序字段映射关系
     */
    private Map<String, Order> getSortedKeyMap() {
        return this.comImportService.getSortedKeyMap();
    }

    private Map<String, Object> getParametersMap() {
        return this.parametersMap;
    }

    /**
     * 获取枚举类型的字段集合
     */
    public Set<String> getEnumFieldSet() {
        return this.enumFieldSet;
    }

    /**
     * 初始化jdbcPagingItemReader
     */
    JdbcPagingItemReader<Map<String, Object>> buildJdbcPagingItemReader(ComBatchExportContext config, DataSource dataSource) {
        String selectStr =
                "select " + config.getExportConfig().getColumns()
                .stream().map(ExcelColCfgDto::getFieldName).collect(Collectors.joining(","));

        String orderStr = String.join(",", getSortedKeyMap().keySet());
        if (Strings.isNotEmpty(orderStr)){
            selectStr = selectStr + "," + orderStr;
        }

        log.info("selectStr:{}", selectStr);

        JdbcPagingItemReaderBuilder<Map<String, Object>> jdbcPagingItemReaderBuilder = new JdbcPagingItemReaderBuilder<>();
        jdbcPagingItemReaderBuilder.fetchSize(FETCH_SIZE).selectClause(selectStr).fromClause("from " + this.getTableName());
        jdbcPagingItemReaderBuilder.whereClause(this.getWhereCondition());
        jdbcPagingItemReaderBuilder.pageSize(PAGE_SIZE)
                .sortKeys(this.getSortedKeyMap())
                .parameterValues(this.getParametersMap())
                .saveState(false)
                .rowMapper(new ComResultRowMapper(this.getFieldNameMapping()))
                .dataSource(dataSource)
                .name(exportConfig.getSheetName());

        return jdbcPagingItemReaderBuilder.build();
    }

    /**
     * 字段名称和字段DTO对象映射转换
     */
    private void processFieldNamesMapping() {
        this.comItemMap.clear();
        Map<String, Object> fields = Maps.newHashMap();
        exportConfig.getColumns().forEach(columnConfig ->
            fields.put(columnConfig.getFieldName(), columnConfig.getTitle()) );
        this.comItemMap = fields;
    }

    /**
     * 加载对应的字典和枚举缓存
     */
    private void processDictFieldCache() {
        Map<String, Object>  enumFieldMap = this.comImportService.getEnumFieldMap();
        if (enumFieldMap != null) {
            enumFieldMap.forEach((key, value) -> this.enumFieldSet.add(key));
        }
    }

    public CommBatchInfoEntity getNewExportJob(Long jobId) {
        this.dirBatchInfoEntity = this.comImportService.getNewExportJob(jobId);
        return this.dirBatchInfoEntity;
    }

    /**
     * 实体类字段和表字段转换
     *
     * @param item 参数
     * @return 转换后的数据
     */
    public String transform(Map<String ,Object> item) {
        return comImportService.transformExport(this, item) ;
    }


    private String getWhereCondition() {
        String defaultConditions = this.comImportService.getDefaultConditions(params);
        String customConditions = this.comImportService.getCustomConditions(params);
        StringJoiner sj = new StringJoiner("");
        if (StringUtils.isNotEmpty(customConditions)) {
            sj.add(customConditions);
        }else {
            sj.add(defaultConditions);
        }
        return sj.toString();
    }

    public CommBatchInfoEntity getDirBatchInfoEntity() {
        return dirBatchInfoEntity;
    }

}
